-- Matías Blanco & Sergio Pérez --
-- Camino de Santiago  -  T. P  --

with Ada.Numerics.Float_Random, Ada.Text_IO;
use Ada.Numerics.Float_Random, Ada.Text_IO;

procedure CaminoTProtAda is


   G : Generator;

   -- Contantes ... tipos y variables ...
   MAX_CIUDADES : constant := 9;
   MAX_PEREGRIN : constant := 10;
   T_CAMINO : constant := 2.0;
   MAX_FUERZAS : constant := 15;
   MAX_DINERO : constant := 6;
   --
   SANTI : Integer := MAX_CIUDADES;


   type TDescanso is (nada,albergue,hotel);


   protected type Ciudad is

      procedure Init(next_cost : Integer;next_c: Integer; plazasA : Integer; F_Recupera_Alb : Integer; HotelCuesta : Integer; F_Recupera_Hot : Integer);
      procedure DormirEnAlbergue (Id : Integer; PuedeDormir : out Boolean; Fuerza : in out Integer);
      procedure SalirDeAlbergue (Id : Integer);
      procedure DormirEnHotel (Id : Integer; Dinero : in out Integer; PuedeDormir : out Boolean; Fuerza : in out Integer);
      procedure SalirDeHotel(Id : Integer);
      --
      function get_next_ciu return Integer;
      function get_next_ciu_cuesta return Integer;


   private
      next_ciu_cuesta : Integer;
      next_ciu : Integer;
      PlazasAlbergue : Integer;
      F_Recupera_Alblbergue : Integer;
      Precio_hotel : Integer;
      F_Recupera_Hotel: Integer;
   end Ciudad;

   protected body Ciudad is

      procedure Init(next_cost : Integer;next_c: Integer; plazasA : Integer; F_Recupera_Alb : Integer; HotelCuesta : Integer; F_Recupera_Hot : Integer) is
      begin
         next_ciu_cuesta := next_cost;
         next_ciu :=next_c ;
         PlazasAlbergue := plazasA;
         F_Recupera_Alblbergue := F_Recupera_Alb;
         Precio_hotel := HotelCuesta;
         F_Recupera_Hotel:= F_Recupera_Hot;
      end Init;

      procedure DormirEnAlbergue (Id : Integer; PuedeDormir : out Boolean; Fuerza : in out Integer) is
      begin
         PuedeDormir := False; -- En un principio no

        if PlazasAlbergue > 0 then
            Put_Line("El peregrino "&Integer'Image(Id)&" consigue sitio en albergue");
            Put_Line("+ "&Integer'Image(F_Recupera_Alblbergue)&" fuerza");
            Put_Line(Integer'Image(PlazasAlbergue)&" plazas");
            plazasAlbergue := PlazasAlbergue - 1;
            Fuerza := Fuerza + F_Recupera_Alblbergue;
            PuedeDormir := True; -- Entonces sí
         end if;
      end DormirEnAlbergue;

      procedure SalirDeAlbergue (Id : Integer) is
      begin
         Put_Line("El peregrino "&Integer'Image(Id)&" sale de albergue");
         PlazasAlbergue := PlazasAlbergue + 1;
      end SalirDeAlbergue;

      procedure DormirEnHotel (Id :Integer; Dinero : in out Integer; PuedeDormir : out Boolean; Fuerza : in out Integer) is
      begin
         PuedeDormir := False; -- Igual que antes ...
         if Dinero >= Precio_hotel then
            Put_Line("El peregrino "&Integer'Image(Id)&" tiene que dormir en un hotel");
            Put_Line("+ " &Integer'Image(F_Recupera_Hotel)&" fuerza");
            Put_Line("- " &Integer'Image(Precio_hotel)&" eurazs");
            PuedeDormir := True;
            Dinero := Dinero - Precio_hotel; -- Será caro?
            Fuerza := Fuerza + F_Recupera_Hotel; -- Será buen hotel ? :)

        end if;
      end DormirEnHotel;

      procedure SalirDeHotel (Id : Integer) is
      begin
         Put_Line("El peregrino "&Integer'Image(Id)&" se va del hotel");
      end SalirDeHotel;


      -- Dos funciones que nos devuelven los valores... información cortesía de Javier Peris :)
      function get_next_ciu return Integer is
      begin
         return next_ciu;
      end get_next_ciu;

      function get_next_ciu_cuesta return Integer is
      begin
         return next_ciu_cuesta;
      end get_next_ciu_cuesta;


   end Ciudad;

   type Camino is array (1 .. MAX_CIUDADES) of Ciudad;

   CaminoDeSantiago : Camino;

   task type Peregrino (Id: Integer);


   type P_Peregrino is access Peregrino;

   Peregrinos : P_Peregrino;

   task body Peregrino is
      Dinero  : Integer;
      Fuerzas : Integer;
      Fracaso : Boolean;
      Tiempo_Dormir : constant := 3.0;
      CiudadActual : Integer;
      PuedeDormir : Boolean;
      Descansa_En : TDescanso;
   begin
      Fuerzas := Integer(Random(G)*Float(MAX_FUERZAS-2))+2;
      Dinero := Integer(Random(G)*Float(MAX_DINERO-1))+1;
      CiudadActual := Integer(Random(G)*2.0)+1;  -- 1, 2 o 3


      Fracaso := False; -- Inicializado a false

      while CiudadActual /= SANTI and not Fracaso loop -- Mientres no llegue y no fracase

         Put_Line("*************************************************************************");
         Put_Line("El peregrino "&Integer'Image(Id)&" esta en "&Integer'Image(CiudadActual));
         Put_Line("Euros - "&Integer'Image(Dinero)&",Fuerza - "&Integer'Image(Fuerzas));
         Put_Line("*************************************************************************");


         Descansa_En := nada; -- Por defecto

         CaminoDeSantiago(CiudadActual).DormirEnAlbergue(Id, PuedeDormir, Fuerzas); -- Albuergue?
         if not PuedeDormir then

            Put_Line("El peregrino "&Integer'Image(Id)&" no encuentra sitio en el albergue");
            CaminoDeSantiago(CiudadActual).DormirEnHotel(Id, Dinero, PuedeDormir, Fuerzas);
            if not PuedeDormir then
               Put_Line("El peregrino "&Integer'Image(Id)&" no puede permitirse el hotel");
            else
               Descansa_En := hotel;
            end if;
         else
            Descansa_En := albergue;
         end if;

         if PuedeDormir then
            delay Duration(Tiempo_Dormir*Random(G));
            if Descansa_En = albergue then CaminoDeSantiago(CiudadActual).SalirDeAlbergue(Id);
            elsif Descansa_En = hotel then CaminoDeSantiago(CiudadActual).SalirDeHotel(Id);
            end if; -- Se va si ha descansado
         else
            Put_Line("El peregrino "&Integer'Image(Id)&" no descansa en "&Integer'Image(CiudadActual));
         end if;


         if Fuerzas < CaminoDeSantiago(CiudadActual).get_next_ciu_cuesta then
            Put_Line("¡¡ POBRE!! ¡¡El peregrino "&Integer'Image(Id)&" fracaso, no tiene fuerzas!!");
            Fracaso := True; -- Jarl
         else -- Si puede...
            if CiudadActual /= SANTI then
               Put_Line("El peregrino "&Integer'Image(Id)&" a por la ciudad "&Integer'Image(CaminoDeSantiago(CiudadActual).get_next_ciu));
               Fuerzas := Fuerzas - CaminoDeSantiago(CiudadActual).get_next_ciu_cuesta; -- Gastamos fuerzas caminando
               Ciudadactual := CaminoDeSantiago(CiudadActual).get_next_ciu; -- Pero merece la pena porque ya estamos aqui
            end if;
         end if;

      end loop; -- while not santiago ...

      if not Fracaso then
         Put_Line("BIENNNNN!!! Peregrino"&Integer'Image(Id)&" ha llegado a Santiago de Compostela!! Rece por nosotros! Para aprobar!!");
      end if;

   end Peregrino;

   procedure InitNodos is
   begin

      -- Informacion del camino, ayuda x J. Peris (gracias)
      CaminoDeSantiago(1).Init(4,3,2,1,3,2);   -- Roncesvalles

      CaminoDeSantiago(2).Init(2,4,2,1,3,1);   -- Somport

      CaminoDeSantiago(3).Init(3,2,1,1,3,1);   -- Nájera

      CaminoDeSantiago(4).Init(5,6,1,2,3,2);   -- Otro

      CaminoDeSantiago(5).Init(3,4,1,2,3,2);   --

      CaminoDeSantiago(6).Init(1,7,1,2,3,2);   --

      CaminoDeSantiago(7).Init(2,8,1,1,3,2);   --

      CaminoDeSantiago(8).Init(3,9,1,1,1,2);   --

      CaminoDeSantiago(9).Init(1000,1000,1000,1000,1000,1000);   -- Ale, llegamos!!!!

   end InitNodos;


begin

   InitNodos;

   for I in 1..MAX_PEREGRIN loop
      Peregrinos := new Peregrino(I);
      delay Duration(Random(G));
   end loop;

end CaminoTProtAda;

